package android.os;

import android.util.Log;
import android.util.Printer;

public class MessageQueue {
    private static final String TAG = "MessageQueueUseNotifyWait";
    private static final boolean DEBUG = false;

    private boolean mQuitting;
    private Message mMessages = null;
    private final Object lock = new Object();
    private int nextPollTimeoutMillis = -1;

    public boolean enqueueMessage(Message msg) {
        if (DEBUG) Log.d(TAG, "enqueue: msg: when = " + msg.when + " " + msg);
        synchronized (lock) {
            Message p = mMessages;
            if (p == null || msg.when == 0 || msg.when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
            } else {
                Message prev;
                for (; ; ) {
                    prev = p;
                    p = p.next;
                    if (p == null || msg.when < p.when) {
                        break;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (DEBUG) Log.d(TAG, "enqueueMessage: notify---------" + lock);
            nextPollTimeoutMillis = 0;
            lock.notify();
        }
        return true;
    }

    public Message next() {
        for (; ; ) {
            synchronized (lock) {
                try {
                    if (DEBUG)
                        Log.d(TAG, "start wait nextPollTimeoutMillis = " + nextPollTimeoutMillis + "--------" + lock);
                    if (nextPollTimeoutMillis < 0) {
                        // 死等
                        lock.wait();
                    } else if (nextPollTimeoutMillis > 0) {
                        // 超时等nextPollTimeoutMillis
                        lock.wait(nextPollTimeoutMillis);
                    } // else nextPollTimeoutMillis = 0; 链表中还有Message未处理，不等
                    if (DEBUG) Log.d(TAG, "waited---------" + lock);
                    final long now = System.currentTimeMillis();
                    Message msg = mMessages;
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                            if (DEBUG)
                                Log.d(TAG, "nextPollTimeoutMillis: " + nextPollTimeoutMillis);
                        } else {
                            // Got a message.
                            mMessages = msg.next;
                            msg.next = null;
                            if (mMessages != null) {
                                nextPollTimeoutMillis = (int) Math.min(mMessages.when - now, Integer.MAX_VALUE);
                                nextPollTimeoutMillis = Math.max(nextPollTimeoutMillis, 0);
                            } else {
                                nextPollTimeoutMillis = -1;
                            }
                            if (DEBUG)
                                Log.d(TAG, "nextPollTimeoutMillis: " + nextPollTimeoutMillis);
                            if (DEBUG) Log.d(TAG, "Returning message: " + msg);
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                        if (DEBUG) Log.d(TAG, "No more messages.");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
    }

    public boolean hasMessages(Handler h, int what, Object object) {
        if (h == null) {
            return false;
        }
        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

    void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.callback == r
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                            && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

    private boolean isPollingLocked() {
        // If the loop is quitting then it must not be idling.
        // We can assume mPtr != 0 when mQuitting is false.
        // TODO:
//        return !mQuitting && nativeIsPolling(mPtr);
        return !mQuitting;
    }

    void dump(Printer pw, String prefix, Handler h) {
        synchronized (this) {
            long now = System.currentTimeMillis();
            int n = 0;
            for (Message msg = mMessages; msg != null; msg = msg.next) {
                if (h == null || h == msg.target) {
                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
                }
                n++;
            }
            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
                    + ", quitting=" + mQuitting + ")");
        }
    }
}